/*
 * @Author: xiaosihan
 * @Date: 2021-07-31 00:57:36
 * @Last Modified by: xiaosihan
 * @Last Modified time: 2021-08-01 23:27:09
 */

import * as THREE from "three";
import { BufferGeometry, Camera, Group, Material, Scene, WebGLRenderer } from "three";

/**
 * 辉光小球
 *  let glowball1 = new GlowBall("#f00");
 *  glowball1.position.set(1,1,0)
 *  glowball1.scale.set(1,1,1)
 */
export default class GlowBall extends THREE.Object3D {

    constructor(color = "#fff") {
        super();
        this.color = color;
        this.init();
    }

    // 颜色
    color = "#fff";

    init() {
        this.add(this.ball1);
        this.add(this.ball2);
        this.add(this.ball3);

        // 时时计算小球的渲染顺序 距离相机近越近缩放值越大 渲染顺序就越大
        this.ball1.onBeforeRender =
            this.ball2.onBeforeRender =
            this.ball3.onBeforeRender =
            function (renderer: WebGLRenderer, scene: Scene, camera: Camera, geometry: BufferGeometry, material: Material, group: Group) {
                camera.getWorldPosition(GlowBall.cameraPosition);
                this.getWorldPosition(GlowBall.ballPosition);
                const { x: scaleX } = this.scale;
                this.renderOrder = scaleX - GlowBall.cameraPosition.distanceTo(GlowBall.ballPosition);
            }

    }

    // 更新世界矩阵时 更新每个小球的缩放值
    updateMatrixWorld(force?: boolean) {

        // 计算交替变化的缩放值
        let timetemp = performance.now() / 5000;
        let scale1 = timetemp % 1;
        let scale2 = (timetemp + 0.33) % 1;
        let scale3 = (timetemp + 0.666) % 1;

        // 时时计算小球的缩放值
        this.ball1.scale.set(scale1, scale1, scale1);
        this.ball2.scale.set(scale2, scale2, scale2);
        this.ball3.scale.set(scale3, scale3, scale3);

        // 设置小球上着色器的 颜色参数
        this.ball1.material.uniforms.color.value.set(this.color);
        this.ball1.material.uniforms.opacity.value = (1 - scale1);

        this.ball2.material.uniforms.color.value.set(this.color);
        this.ball2.material.uniforms.opacity.value = (1 - scale2);

        this.ball3.material.uniforms.color.value.set(this.color);
        this.ball3.material.uniforms.opacity.value = (1 - scale3);

        super.updateMatrixWorld(force);
    }



    // 辉光材质
    static ballMaterial = new THREE.ShaderMaterial({
        transparent: true,
        side: THREE.FrontSide,
        uniforms: {
            color: {
                value: new THREE.Color("#fff")
            },
            coeficient: {
                value: 0.8
            },
            power: {
                value: 1
            },
            opacity: {
                value: 1
            },
        },
        vertexShader: `
        varying vec3  vVertexWorldPosition;
        varying vec3  vVertexNormal;
        varying vec4  vFragColor;
        void main(){
            //将法线转换到视图坐标系中
            vVertexNormal = normalize(normalMatrix * normal);
            //将顶点转换到世界坐标系中
            vVertexWorldPosition = (modelMatrix * vec4(position, 1.0)).xyz;
            gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
        }
        `,
        fragmentShader: `
        uniform vec3  color;
        uniform float    coeficient;
        uniform float    power;
        uniform float    opacity;

        varying vec3 vVertexNormal;
        varying vec3 vVertexWorldPosition;
        varying vec4 vFragColor;
        
        void main(){
            if (vVertexWorldPosition.y < 0.0) {
                discard;
            }
            vec3 worldCameraToVertex = vVertexWorldPosition - cameraPosition; //世界坐标系中从相机位置到顶点位置的距离
            vec3 viewCameraToVertex = (viewMatrix * vec4(worldCameraToVertex, 0.0)).xyz; //视图坐标系中从相机位置到顶点位置的距离
            viewCameraToVertex = normalize(viewCameraToVertex); //规一化
            float intensity = pow(coeficient + dot(vVertexNormal, viewCameraToVertex), power);
            gl_FragColor = vec4(color, intensity * opacity);
        }
        `
    });

    // 小球模型
    static ballGeo = new THREE.SphereBufferGeometry(1, 100, 100);

    // 相机的位置
    static cameraPosition = new THREE.Vector3();

    // 小球的位置
    static ballPosition = new THREE.Vector3();

    ball1 = new THREE.Mesh(GlowBall.ballGeo, GlowBall.ballMaterial.clone());

    ball2 = new THREE.Mesh(GlowBall.ballGeo, GlowBall.ballMaterial.clone());

    ball3 = new THREE.Mesh(GlowBall.ballGeo, GlowBall.ballMaterial.clone());


}